﻿// CommonConnectionPool.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//函数的实现
#include "pch.h"
#include <iostream>
#include "CommonConnectionPool.h"
#include "public.h"
#include <string>
#include <queue> 
#include <mutex>


using namespace std;

//线程安全的懒汉单例函数接口
ConnectionPool* ConnectionPool::getConnectionPool()
{
    static ConnectionPool pool;//对静态对象（静态局部变量）的初始化由编译器自动进行lock和unlock
    return &pool;
}

//从配置文件中加载配置项 失败或成功
bool ConnectionPool::loadConfigFile()
{
    FILE* pf = fopen("mysql.ini", "r");
    if (pf == nullptr)
    {
        LOG("配置文件加载失败");
        return false;
    }

    while (!feof(pf))//如果链接文件成功
    {
        char line[1024] = { 0 };//接受一行数据
        fgets(line, 1024, pf);
        string str = line;//取子串方法？
        int idx = str.find('=', 0);//找ini文件里面的键值对
        if (idx == -1)//无效的配置项
        {
            continue;
        }

        //password=123456 前面是key 后面是value
        int endidx = str.find('\n', idx); //此时idx饭hi等号前的个数 先声明再用，别直接写
        string key = str.substr(0, idx);
        string  value = str.substr(idx + 1, endidx - idx - 1);//下标比个数少一个

        if (key == "ip") {
            _ip = value;
        }
        else if (key == "port") {
            _port = atoi(value.c_str());//转成整数
        }
        else if (key == "username") {
            _username = value;
        }
        else if (key == "dbname") {
            _dbname = value;
        }
        else if (key == "password") {
            _password = value;
        }
        else if (key == "initSize") {
            _initSize = atoi(value.c_str());
        }
        else if (key == "maxSize") {
            _maxSize = atoi(value.c_str());
        }

        else if (key == "connectionTimeout") {
            _ConnectionTimeout = atoi(value.c_str());
        }
    }
    return true;
}
//连接池的构造
 ConnectionPool::ConnectionPool()
        {
            //加载配置项
            if (!loadConfigFile())
            {
                return;
            }
            //创建初始数量的连接
            for (int i = 0;i < _initSize;++i) {
                Connection* p = new Connection();
                p->connect(_ip, _port, _username, _dbname,_password);
                p->refreshAliveTime();//刷新开始空闲的起始时间
                _connectionQue.push(p);
                _connectionCnt++;
            }

            //启动一个新的线程，作为连接的生产者
            thread produce(std::bind(&ConnectionPool::produceConnectionTask,this));//绑定当前对象
            produce.detach();//设成分离线程

            //启动一个新的定时线程，扫描超过maxIdleTime时间的空闲连接，进行对于连接的回收
            thread scanner(std::bind(&ConnectionPool::scannerConnectionTask, this));//绑定当前对象
            scanner.detach();//设成分离线程

}
 //运行在独立线程中，专门负责生产新连接
 void ConnectionPool::produceConnectionTask()
 {
     for (;;)
     {
         unique_lock<mutex> lock(_queueMutex);
         while (!_connectionQue.empty())
         {
             cv.wait(lock);//队列不空，此处生产线程进入等待状态
         }
         //连接数量未到上限，继续创建新连接
         if (_connectionCnt < _maxSize)
         {
                 Connection* p = new Connection();
                 p->connect(_ip, _port,_username, _password,_dbname);
                 p->refreshAliveTime();//刷新开始空闲的起始时间
                 _connectionQue.push(p);//push到队列中
                 _connectionCnt++;
             
         }
         //通知消费线程可以消费连接
         cv.notify_all();
     }
 }

 /*shared_ptr<Connection> sp = cp->getConnection();
 sp->undate(sql);
 }*/

 ////给外部提供接口，从连接池中获取一个可用的空闲连接
 shared_ptr<Connection> ConnectionPool::getConnection()
 {
     unique_lock<mutex>lock(_queueMutex);
     while (_connectionQue.empty()) //一上来是空，那就进行等待，直到等到超时
      {
         if (cv_status::timeout == cv.wait_for(lock, chrono::milliseconds(_ConnectionTimeout)))//超时醒来发现还是空的
         {
             if (_connectionQue.empty())
             {
                 LOG("获取空闲连接超时了...")
                     return nullptr;
             }
         }//在指定时间内，如果有可获取的连接，就把他拿走
        
     }
     /* 
     shared_ptr指针析构时，会把connection资源直接delete掉,相当于调用connection的析构函数，
     connection就被close了，这里需要自定义shared_ptr的释放资源方式，把connection直接归还到queue中
     
     */
     shared_ptr<Connection>sp(_connectionQue.front(), [&](Connection* pcon)
         {//这是在服务器应用线程中调用，一定要考虑队列的线程安全
             unique_lock<mutex> lock(_queueMutex);
             pcon->refreshAliveTime();//刷新开始空闲的起始时间
             _connectionQue.push(pcon);//归还队列
         });
     _connectionQue.pop();//创建一个智能指针把_connectionQue出队//消费了
     if (_connectionQue.empty())//如果队列为空了，通知生产者赶紧生产
     {
         cv.notify_all();//谁消费完最后一个connection,谁负责通知生产者生产连接
     }
     return sp;
 }

 //扫描超过maxIdleTime时间的空闲连接，进行对于连接的回收
 void ConnectionPool::scannerConnectionTask()
 {
     for (;;)
     {
         //通过sleep模拟定时效果
         this_thread::sleep_for(chrono::seconds(_maxIdleTime));//设置一个定时

         //扫描整个队列，释放多余的连接
         unique_lock<mutex>lock(_queueMutex);//加一个控制锁，保护线程安全
         while (_connectionCnt > _initSize)
         {
             Connection* p = _connectionQue.front();//获取一下队头
             if (p->getAliveTime() >= (_maxIdleTime * 1000))//如果队头元素存活时间超过了这个时间
             {
                 _connectionQue.pop();
                 _connectionCnt--;
                 delete(p);//调用~connection（）释放连接
             }
             else
             {
                 break;//队头的连接没超过_maxIdleTime ，其他连接定然也没超过
             }
         }
     }
 }


